{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Долг с прошлой лекции:\n",
    "\n",
    "1. Возвращаемое значение:\n",
    "\n",
    "```c++\n",
    "bool append_teamlead(Point3D location, std::vector<std::string>& team)\n",
    "{\n",
    "    if (is_inside(location, get_village(\"Shire\")))\n",
    "    {\n",
    "        team.push_back(\"Thorin\");\n",
    "        return true;\n",
    "    }\n",
    "    return false;\n",
    "}\n",
    "```\n",
    "\n",
    "2. Битовые операции в С++:\n",
    "\n",
    "Память - последовательность битов - 1 и 0. Битовые операции определены для целых чисел.\n",
    "\n",
    "```c++\n",
    "// SHL\n",
    "uint8_t x = 42; // 00101010\n",
    "x << 1;         // 01010100 == 42 * (2**1)\n",
    "x << 2;         // 10101000 == 42 * (2**2)\n",
    "\n",
    "// SHR\n",
    "uint8_t x = 42; // 00101010\n",
    "x >> 1;         // 00010101 == 42 / (2**1)\n",
    "x >> 2;         // 00001010 == 42 / (2**2)\n",
    "x >> 3;         // 00000101 == 42 / (2**3)\n",
    "x >> 4;         // 00000010 == 42 / (2**4)\n",
    "\n",
    "//\n",
    "std::uint8_t x = 42; // 00101010\n",
    "std::uint8_t y = 55; // 00110111\n",
    "x & y;               // 00100010\n",
    "x | y;               // 00111111\n",
    "x ^ y;               // 00011101\n",
    "~x;                  // 11010101\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Лекция 3. Классы. Введение"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Класс и объект - ключевые термины в ООП - языках"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](classes.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Барсик - объект класса \"Кошка домашняя\"\n",
    "* \"Кошка домашняя\" - класс, наследник класса \"Кошки\"\n",
    "* \"Кошки\" - класс, наследник класса \"Кошачьи\"\n",
    "* \"Кошачьи\" - класс, наследник класса \"Хищные\"\n",
    "\n",
    "\n",
    "Объект класса - конкретный элемент этого типа.\n",
    "\n",
    "Класс-наследник __уточняет__ характеристики базового класса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для описания классов в языке С++ есть два ключевых слова: `class` и `struct`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "    ...\n",
    "};\n",
    "\n",
    "struct Point\n",
    "{\n",
    "    ...\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Класс может иметь данные: члены класса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "    std::string name;\n",
    "    unsigned age;\n",
    "};\n",
    "\n",
    "struct Point\n",
    "{\n",
    "    float x;\n",
    "    float y;\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как создавать объекты таких классов:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "Animal a;\n",
    "Point p;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Доступ до полей:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "p.x = 3.f;\n",
    "p.y = 4.f;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Класс может иметь методы:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "    bool is_too_young() const\n",
    "    {\n",
    "        return age == 0;\n",
    "    }\n",
    "   \n",
    "    std::string name;\n",
    "    unsigned age;\n",
    "};\n",
    "\n",
    "//\n",
    "// или так:\n",
    "//\n",
    "\n",
    "class Animal\n",
    "{\n",
    "    bool is_too_young() const;\n",
    "    \n",
    "    std::string name;\n",
    "    unsigned age;\n",
    "};\n",
    "\n",
    "bool Animal::is_too_young() const\n",
    "{\n",
    "    return age == 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Класс может наследоваться от другого класса:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Cat : Animal\n",
    "{\n",
    "};\n",
    "\n",
    "struct WeightedPoint : Point\n",
    "{\n",
    "    float w;\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "У членов и методов классов есть модификаторы видимости:\n",
    "* `private` - доступ только себе самому и друзьям\n",
    "* `protected` - плюс наследники\n",
    "* `public` - кто угодно"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Отличие `class` от `struct` (с точки зрения компилятора) - модификатор видимости по умолчанию для методов/членов и при наследовании.\n",
    "* `class` -> `private`\n",
    "* `struct` -> `public`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "    bool is_too_young() const { return age == 0; }\n",
    "  \n",
    "private:\n",
    "    std::string name;\n",
    "    unsigned age;\n",
    "};\n",
    "\n",
    "class Cat : public Animal\n",
    "{\n",
    "public:\n",
    "    void say_meow() const { std::cout << \"meow\"; }\n",
    "};\n",
    "\n",
    "struct Point\n",
    "{\n",
    "    float x;\n",
    "    float y;\n",
    "};\n",
    "\n",
    "struct WeightedPoint : Point\n",
    "{\n",
    "    float w;\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "Cat barsic;\n",
    "barsic.say_meow();      // OK\n",
    "barsic.name = \"Barsic\"; // Compile-time error: name is private member\n",
    "    \n",
    "WeightedPoint p;\n",
    "p.x = 3.f;  // OK\n",
    "p.w = 0.f;  // OK\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Иногда приходится создавать объекты классов, копировать и уничтожать их. Для этого существуют конструкторы и деструкторы:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для каждого класса могут быть определены (или удалены) основные операции:\n",
    "* конструктор по умолчанию (default constructor)\n",
    "* копирующий конструктор (copy constructor)\n",
    "* перемещающий конструктор (move constructor)\n",
    "* копирующий оператор присваивания (copy assignment)\n",
    "* перемещающий оператор присваивания (move assignment)\n",
    "* деструктор (destructor)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "private:\n",
    "    std::string name;\n",
    "    unsigned age;\n",
    "    \n",
    "public:\n",
    "    // конструктор по умолчанию\n",
    "    //\n",
    "    // Animal an;\n",
    "    Animal() : age(0) {}\n",
    "    // что с name?\n",
    "\n",
    "    // конструктор из одного аргумента\n",
    "    //\n",
    "    // Animal an(\"Murka\");\n",
    "    Animal(const std::string& a_name)\n",
    "        : name(a_name)\n",
    "        , age(0)\n",
    "    {}\n",
    "\n",
    "    // конструктор из двух аргументов\n",
    "    //\n",
    "    // Animal an(\"Murka\", 7);\n",
    "    Animal(const std::string& a_name, const int a_age)\n",
    "        // список инициализации для членов класса\n",
    "        // плюс, возможно, спецификация вызова базового класса\n",
    "        : name(a_name)\n",
    "        , age(a_age)\n",
    "    {\n",
    "        // тело конструктора\n",
    "        std::cout << \"creating animal\" << std::endl;\n",
    "        std::cout << \"animal created\" << std::endl;\n",
    "    }  // когда выполнение доходит до конца конструктора, объект класса Animal считается сконструированным\n",
    "    \n",
    "    // конструктор копирования\n",
    "    //\n",
    "    // Animal a1;  // default\n",
    "    // Animal a2 = a1;\n",
    "    // Animal a3(a2);\n",
    "    // имеет доступ к приватным полям rhs, т.к. объект того же класса\n",
    "    Animal(const Animal& rhs)\n",
    "        : name(rhs.name)\n",
    "        , age(rhs.age)\n",
    "    {}\n",
    "    \n",
    "    // конструктор перемещения\n",
    "    //\n",
    "    // Animal a1(\"Murka\", 13);\n",
    "    // Animal a2 = std::move(a1);\n",
    "    // a2 - ? a1 - ?\n",
    "    Animal(Animal&& rhs)\n",
    "        : name(std::move(rhs.name))\n",
    "        , age(rhs.age)\n",
    "    {}\n",
    "    \n",
    "    // копирующее присваивание\n",
    "    //\n",
    "    // Animal a1(\"Murka\", 13), a2;\n",
    "    // a2 = a1;\n",
    "    Animal& operator = (const Animal& rhs)\n",
    "    {\n",
    "        ...;\n",
    "        return *this;\n",
    "    }\n",
    "    \n",
    "    // перемещающее присваивание\n",
    "    //\n",
    "    // Animal a1(\"Murka\", 13), a2;\n",
    "    // a2 = std::move(a1);\n",
    "    Animal& operator = (Animal&& rhs)\n",
    "    {\n",
    "        ...;\n",
    "        return *this;\n",
    "    }\n",
    "    \n",
    "    // деструктор\n",
    "    //\n",
    "    // обратить внимание, что в деструкторе происходит больше операций, чем вывод, объяснить, почему.\n",
    "    ~Animal()\n",
    "    {\n",
    "        std::cout << \"destroying \" << name << std::endl;\n",
    "    }\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Когда вызываются конструкторы и деструкторы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Конструктор класса вызывается в момент его создания. Пока конструктор класса не отработает, объектом пользоваться нельзя.\n",
    "\n",
    "Деструктор класса вызывается, когда объект покидает свою область видимости."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "void fun()\n",
    "{\n",
    "    Grasshopper barsic1(\"Barsic\", 1);\n",
    "    \n",
    "    {\n",
    "        Grasshopper barsic2(\"Barsic\", 2);\n",
    "    }\n",
    "    \n",
    "    Grasshopper barsic3(\"Barsic\", 3);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "void fun()\n",
    "{\n",
    "    Grasshopper* barsic = new Grasshopper(\"Barsic\", 1);\n",
    "    \n",
    "    for (int i = 0; i < 10; ++i)\n",
    "    {\n",
    "        Grasshopper barsic2(\"Barsic\", i);\n",
    "    }\n",
    "}\n",
    "// Обсудить:\n",
    "//    1. последовательность конструкторов-деструкторов\n",
    "//    2. какая проблема\n",
    "//    3. как поправить\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "void fun()\n",
    "{\n",
    "    Grasshopper* barsics = new Grasshopper[3];\n",
    "    \n",
    "    if (Grasshopper h = make_grasshopper(); h.is_alive())\n",
    "    {\n",
    "        std::cout << \"it is alive!\" << std::endl;\n",
    "    }\n",
    "    else\n",
    "    {\n",
    "        std::cout << \"\" << std::endl;\n",
    "    }\n",
    "    \n",
    "    delete barsics;\n",
    "}\n",
    "// Обсудить:\n",
    "//    1. последовательность конструкторов-деструкторов\n",
    "//    2. какая проблема\n",
    "//    3. как поправить\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### RAII (Resource Acqusition Is Initialization)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "RAII - ключевая идиома в С++: захват ресурса должен совпадать с инициализацией объекта, а освобождение ресурса с его разрушением."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Т.о.:\n",
    "* конструктор объекта захватывает ресурс\n",
    "* деструктор объекта его освобождает"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Пример 1**: массив на куче\n",
    "\n",
    "```c++\n",
    "class ArrayD\n",
    "{\n",
    "private:\n",
    "    double* data;\n",
    "    int size;\n",
    "    \n",
    "public:\n",
    "    ArrayD(int n)\n",
    "        : data(new double[n]),\n",
    "        , size(n)\n",
    "    {\n",
    "    }\n",
    "    \n",
    "    ~ArrayD()\n",
    "    {\n",
    "        delete[] data;\n",
    "    }    \n",
    "};\n",
    "\n",
    "void func()\n",
    "{\n",
    "    ArrayD arr(100);    \n",
    "    ...\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь вы можете представить как работает такой код с памятью и почему он корректен:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "void fun()\n",
    "{\n",
    "    std::vector<std::string> team = {\"Dobrynia\", \"Ilusha\", \"Alesha\"};\n",
    "    \n",
    "    for (const auto& member : team)\n",
    "        std::cout << member << std::end;    \n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Пример 2**: С++-обёртка над С-файлом\n",
    "\n",
    "```c++\n",
    "class File\n",
    "{\n",
    "private:\n",
    "    FILE* file;\n",
    "    \n",
    "public:\n",
    "    File(const char* filename) : file(std::fopen(filename, \"w+\"))\n",
    "    {\n",
    "    }\n",
    "    \n",
    "    ~File()\n",
    "    {\n",
    "        if (file)\n",
    "            std::fclose(file);\n",
    "    }\n",
    "    ...\n",
    "};\n",
    "\n",
    "\n",
    "void fun()\n",
    "{\n",
    "    File f(\"input.txt\");\n",
    "    ...\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Какую часть работы по составлению конструкторов, деструкторов и операторов присваивания можно отдать компилятору?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Компилятор может сгенерировать эти операции в дефолтном виде **по полям**, правила такие:\n",
    "\n",
    "Что имеется ввиду под фразой **по полям** :\n",
    "\n",
    "```c++\n",
    "class Animal\n",
    "{\n",
    "    std::string name = \"UNK\";\n",
    "    int age = 0;\n",
    "};\n",
    "\n",
    "Animal an1;           // значение?\n",
    "Animal an2(an1);      // значение?\n",
    "an1 = an2;            // значение?\n",
    "an2 = std::move(an1); // значение?\n",
    "```\n",
    "\n",
    "* автогенерируемый дефолтный конструктор устанавливает полям дефолтные значения\n",
    "* автогенерируемый конструктор копирования копирует значения по полям\n",
    "* автогенерируемый конструктор перемещения перемещает значения по полям\n",
    "* автогенерируемый оператор присваивания ... (вопрос к студентам)\n",
    "* автогенерируемый оператор перемещения ... (вопрос к студентам)\n",
    "* автогенерируемый деструктор ... (вопрос к студентам)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Вопрос__: В каких случаях нужно избежать автогенерированного конструктора копирования? Пример?\n",
    "\n",
    "<details>\n",
    "<summary>Ответ 1</summary>\n",
    "<p>\n",
    "\n",
    "Когда копирование по полям некорректно копирует состояние класса. Пример:\n",
    "\n",
    "```c++\n",
    "class String\n",
    "{\n",
    "    const char* data;\n",
    "    unsigned int len;\n",
    "};\n",
    "```\n",
    "\n",
    "</p>\n",
    "</details>\n",
    "\n",
    "<details>\n",
    "<summary>Ответ 2</summary>\n",
    "<p>\n",
    "\n",
    "Когда копирование запрещено. Пример:\n",
    "\n",
    "```c++\n",
    "class File\n",
    "{\n",
    "    FILE* file;\n",
    "};\n",
    "```\n",
    "\n",
    "</p>\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Вопрос для продвинутых__: В каких ещё случаях (кроме ручного deepcopy) нужно избежать автогенерированного конструктора перемещения? Пример?\n",
    "\n",
    "<details>\n",
    "<summary>Ответ</summary>\n",
    "<p>\n",
    "\n",
    "Когда есть зависимость по полям. Пример:\n",
    "\n",
    "\n",
    "```c++\n",
    "class ListWithZerosCount\n",
    "{\n",
    "    std::list<int> list;\n",
    "    int zeros_count;\n",
    "};\n",
    "\n",
    "\n",
    "ListWithZerosCount l1 = ...;\n",
    "\n",
    "ListWithZerosCount l2 = std::move(l1);\n",
    "\n",
    "// можем продолжить работу с l1\n",
    "```\n",
    "\n",
    "Объяснение: перемещение всегда оставляет перемещаемый объект в unspecified valid state (если специально не оговорено иначе). Если по полям есть зависимость, перевод всех полей в unspecified valid state mможет нарушить корректность самого класса.\n",
    "\n",
    "</p>\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Правила автогенерации:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](comp_rules.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Но никто их не помнит наизусть, поэтому работает правило 6:\n",
    "    \n",
    "__Определил одно - определи все шесть__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Замечание:** далее рисовать происходящее в памяти, так понимается лучше"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Мой первый класс"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Реализуем класс - строку на С++.\n",
    "\n",
    "Важнейший принцип языка, основа его основ - RAII (Resource Acquisition Is Initialization)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class String\n",
    "{\n",
    "public:\n",
    "    String();\n",
    "    String(const char *s);\n",
    "    String(const char *s, int size);\n",
    "    ~String();\n",
    "    String(const String& rhs);\n",
    "    String(String&& rhs);\n",
    "\n",
    "    String& operator = (const String& rhs);\n",
    "    String& operator = (String&& rhs);\n",
    "\n",
    "    friend String operator + (const String& lhs, const String& rhs);\n",
    "\n",
    "private:\n",
    "    char* s_;  // pointer to null-terminated characters\n",
    "    size_t l_; // strlen(s_) == l_\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для начала реализуем оператор сложения двух строк"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String operator + (const String& lhs, const String& rhs)\n",
    "{\n",
    "    const size_t res_size = lhs.l_ + rhs.l_;\n",
    "    const char* res_s = new char[res_size + 1];\n",
    "    strcpy(res_s, lhs.s_);\n",
    "    strncpy(res_s + lhs.l_, rhs.s_, rhs.l_);\n",
    "    \n",
    "    String s(res_s, res_size);\n",
    "\n",
    "    delete[] res_s;\n",
    "    return s;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**конструкторы** - код, вызываемый при создании объекта"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::String()\n",
    "    : s_(new char[1])\n",
    "    , l_(0)\n",
    "{\n",
    "    s_[0] = 0;\n",
    "}\n",
    "\n",
    "String::String(const char* s)\n",
    "{\n",
    "    l_ = strlen(s);\n",
    "    s_ = new char[l + 1];\n",
    "    strcpy(s_, s);\n",
    "}\n",
    "\n",
    "String::String(const char *s, int size)\n",
    "    : s_(new char[size + 1])\n",
    "    , l_(size + 1)\n",
    "{\n",
    "    strncpy(s_, s, size);\n",
    "    s_[l_] = 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**деструктор** - код, который будет вызываться при уничтожении объекта"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::~String()\n",
    "{\n",
    "    delete[] s_;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Остановимся на секунду и всомним про RAII"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**конструктор копирования**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::String(const String& rhs)\n",
    "{\n",
    "    s_ = new char[rhs.l_ + 1];\n",
    "    l_ = rhs.l_;\n",
    "    strcpy(s_, rhs.s_);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**конструктор перемещения**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::String(String&& rhs)\n",
    "    : s_(rhs.s_)\n",
    "    , l_(rhs.l_)\n",
    "{\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Что-то пошло не так. Что именно?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Не работает такой код, а должен:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "{\n",
    "    String s1 = \"run, Forest, run!\";\n",
    "    String s2 = std::move(s1);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Реализуем правильный конструктор перемещения"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::String(String&& rhs)\n",
    "    : s_(rhs.s_)\n",
    "    , l_(rhs.l_)\n",
    "{\n",
    "    rhs.s_ = 0;\n",
    "    rhs.l_ = 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "А точно ли он правильный?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary>Ответ:</summary>\n",
    "\n",
    "rhs сломан\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Третья попытка реализовать правильный конструктор перемещения"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::String(String&& rhs)\n",
    "{\n",
    "    s_ = rhs.s_;\n",
    "    l_ = rhs.l_;\n",
    "\n",
    "    rhs.s_ = new char[1];\n",
    "    rhs.s_[0] = 0;\n",
    "    rhs.l_ = 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**копирующее присваивание**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String& String::operator =(const String& rhs)\n",
    "{\n",
    "    s_ = new char[rhs.l_ + 1];\n",
    "    l_ = rhs.l_;\n",
    "    strcpy(s_, rhs.s_);\n",
    "\n",
    "    return *this;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary>Где ошибка?</summary>\n",
    "\n",
    "Утекает предыдущий массив `s_`\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String& String::operator =(const String& rhs)\n",
    "{\n",
    "    delete[] s_;\n",
    "\n",
    "    s_ = new char[rhs.l_ + 1];\n",
    "    l_ = rhs.l_;\n",
    "    strcpy(s_, rhs.s_);\n",
    "\n",
    "    return *this;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary>Где ошибка?</summary>\n",
    "\n",
    "Самоприсваивание\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Правильная реализация будет выглядеть так:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String& String::operator =(const String& rhs)\n",
    "{\n",
    "    if (this != &rhs)\n",
    "    {\n",
    "        delete[] s_;\n",
    "\n",
    "        s_ = new char[rhs.l_ + 1];\n",
    "        l_ = rhs.l_;\n",
    "        strcpy(s_, rhs.s_);\n",
    "    }\n",
    "    return *this;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**перемещающее присваивание**:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вариант 1:** с очисткой `rhs`\n",
    "\n",
    "```c++\n",
    "String& String::operator =(String&& rhs)\n",
    "{\n",
    "    if (this != &rhs)\n",
    "    {\n",
    "        delete[] s_;\n",
    "\n",
    "        s_ = rhs.s_;\n",
    "        l_ = rhs.l_;\n",
    "\n",
    "        rhs.s_ = new char[1];\n",
    "        rhs.s_[0] = 0;\n",
    "        rhs.l_ = 0;\n",
    "    }\n",
    "\n",
    "    return *this;\n",
    "}\n",
    "```\n",
    "\n",
    "**Вариант 2:** обмен с `rhs`\n",
    "\n",
    "```c++\n",
    "String& String::operator =(String&& rhs)\n",
    "{\n",
    "    std::swap(s_, rhs.s_);\n",
    "    std::swap(l_, rhs.l_);\n",
    "    return *this;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Порядок конструирования"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "#include <iostream>\n",
    "\n",
    "class Name {\n",
    "public:\n",
    "    Name() { std::cout << \"Name \"; }\n",
    "    ~Name() { std::cout << \"~Name \"; }\n",
    "};\n",
    "\n",
    "class Leg {\n",
    "public:\n",
    "    Leg() { std::cout << \"Leg \"; }\n",
    "    ~Leg() { std::cout << \"~Leg \"; }\n",
    "};\n",
    "\n",
    "class Hat {\n",
    "public:\n",
    "    Hat() { std::cout << \"Hat \"; }\n",
    "    ~Hat() { std::cout << \"~Hat \"; }\n",
    "};\n",
    "\n",
    "class Animal {\n",
    "public:\n",
    "    Animal() { std::cout << \"Animal \"; }\n",
    "    ~Animal() { std::cout << \"~Animal \"; }\n",
    "\n",
    "private:\n",
    "    Name name_;\n",
    "};\n",
    "\n",
    "class Turtle : public Animal {\n",
    "public:\n",
    "    Turtle() { std::cout << \"Turtle \"; }\n",
    "    ~Turtle() { std::cout << \"~Turtle \"; }\n",
    "\n",
    "private:\n",
    "    Leg l1_, l2_, l3_, l4_;\n",
    "    Hat hat_;\n",
    "};\n",
    "\n",
    "// show this later\n",
    "// Animal animal;\n",
    "\n",
    "int main() {\n",
    "    std::cout << \"hello!\" << std::endl;\n",
    "    Turtle tortilla;\n",
    "    std::cout << std::endl << \"goodbye!\" << std::endl;\n",
    "    return 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Порядок вызова конструкторов:\n",
    "1. сначала базовый класс\n",
    "2. потом члены в порядке их объявления\n",
    "3. потом конструктор самого класса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Порядок вызова деструкторов:\n",
    "* обратно порядку вызова конструкторов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ожидаемый вывод:\n",
    "\n",
    "```sh\n",
    "hello!\n",
    "Name Animal Leg Leg Leg Leg Hat Turtle\n",
    "goodbye!\n",
    "~Turtle ~Hat ~Leg ~Leg ~Leg ~Leg ~Animal ~Name\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
